/*
** $Id: lapi.c,v 2.259.1.2 2017/12/06 18:35:12 roberto Exp $
** Lua API
** See Copyright Notice in lua.h
*/

#define lapi_c
#define LUA_CORE

#include "lprefix.h"


#include <stdarg.h>
#include <string.h>

#include "lua.h"

#include "lapi.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lundump.h"
#include "lvm.h"


const char lua_ident[] =
        "$LuaVersion: " LUA_COPYRIGHT " $"
        "$LuaAuthors: " LUA_AUTHORS " $";


/* value at a non-valid index */
#define NONVALIDVALUE    cast(TValue *, luaO_nilobject)

/* corresponding test */
#define isvalid(o)  ((o) != luaO_nilobject)

/* test for pseudo index */
#define ispseudo(i)    ((i) <= LUA_REGISTRYINDEX)

/* test for upvalue */
#define isupvalue(i)    ((i) < LUA_REGISTRYINDEX)

/* test for valid but not pseudo index */
#define isstackindex(i, o)  (isvalid(o) && !ispseudo(i))

#define api_checkvalidindex(l, o)  api_check(l, isvalid(o), "invalid index")

#define api_checkstackindex(l, i, o)  \
  api_check(l, isstackindex(i, o), "index not in the stack")

/**
 * @brief 根据给定的索引值，返回对应的 Lua 栈中的地址。
 *
 * @param L Lua 状态机指针。
 * @param idx 索引值，可以是正数、负数或伪索引（如 LUA_REGISTRYINDEX）。
 * @return TValue* 返回指向栈中对应位置的指针；如果索引无效，返回 NONVALIDVALUE。
 *
 * @details
 * - 正数索引：从当前调用帧的函数位置开始计算。
 * - 负数索引：从栈顶开始反向计算。
 * - 伪索引：
 *   - LUA_REGISTRYINDEX：返回全局注册表的地址。
 *   - 其他伪索引：用于访问闭包的上值（upvalue）。
 *
 * @note
 * - 函数会检查索引的有效性，无效索引会触发 API 检查错误。
 * - 对于轻量级 C 函数（light C function），不支持上值访问。
 */
static TValue *index2addr(lua_State *L, int idx)
{
    CallInfo *ci = L->ci;
    if (idx > 0)
    {
        TValue *o = ci->func + idx;
        api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");
        if (o >= L->top) return NONVALIDVALUE;
        else return o;
    }
    else if (!ispseudo(idx))
    { /* negative index */
        api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
        return L->top + idx;
    }
    else if (idx == LUA_REGISTRYINDEX)
        return &G(L)->l_registry;
    else {  /* upvalues */
        idx = LUA_REGISTRYINDEX - idx;
        api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
        if (ttislcf(ci->func))  /* light C function? */
            return NONVALIDVALUE;  /* it has no upvalues */
        else {
            CClosure *func = clCvalue(ci->func);
            return (idx <= func->nupvalues) ? &func->upvalue[idx - 1] : NONVALIDVALUE;
        }
    }
}

/**
 * 在保护模式下由 'lua_checkstack' 调用，用于扩展堆栈并捕获内存错误。
 * @param L Lua 状态机指针。
 * @param ud 用户数据，指向一个整数，表示需要扩展的堆栈大小。
 */
/*
** to be called by 'lua_checkstack' in protected mode, to grow stack
** capturing memory errors
*/
static void growstack(lua_State *L, void *ud) {
    int size = *(int *) ud;
    luaD_growstack(L, size);
}


/**
 * 检查并确保 Lua 栈有足够的空间容纳至少 `n` 个新元素。
 *
 * @param L Lua 状态机指针。
 * @param n 需要的最小栈空间（元素数量）。
 * @return 如果栈空间足够或成功扩展栈，返回 1；否则返回 0。
 *
 * @note 如果栈空间不足，函数会尝试扩展栈。扩展失败时返回 0。
 * @note 此函数是线程安全的，调用时会加锁。
 * @warning 如果 `n` 为负数，会触发 API 检查错误。
 */
LUA_API int lua_checkstack(lua_State *L, int n) {
    int res;
    CallInfo *ci = L->ci;
    lua_lock(L);
    api_check(L, n >= 0, "negative 'n'");
    if (L->stack_last - L->top > n)  /* stack large enough? */
        res = 1;  /* yes; check is OK */
    else {  /* no; need to grow stack */
        int inuse = cast_int(L->top - L->stack) + EXTRA_STACK;
        if (inuse > LUAI_MAXSTACK - n)  /* can grow without overflow? */
            res = 0;  /* no */
        else  /* try to grow stack */
            res = (luaD_rawrunprotected(L, &growstack, &n) == LUA_OK);
    }
    if (res && ci->top < L->top + n)
        ci->top = L->top + n;  /* adjust frame top */
    lua_unlock(L);
    return res;
}


/**
 * 将指定数量的值从一个 Lua 状态移动到另一个 Lua 状态。
 *
 * @param from 源 Lua 状态，值将从该状态中移出。
 * @param to 目标 Lua 状态，值将被移动到该状态。
 * @param n 需要移动的值的数量。
 *
 * @note 如果 `from` 和 `to` 是同一个状态，函数将直接返回。
 * @note 源状态和目标状态必须属于同一个全局环境（G），否则会触发错误。
 * @note 目标状态的栈空间必须足够容纳 `n` 个值，否则会触发栈溢出错误。
 *
 * @warning 此函数会修改源状态和目标状态的栈顶指针。
 */
LUA_API void lua_xmove(lua_State *from, lua_State *to, int n) {
    int i;
    if (from == to) return;
    lua_lock(to);
    api_checknelems(from, n);
    api_check(from, G(from) == G(to), "moving among independent states");
    api_check(from, to->ci->top - to->top >= n, "stack overflow");
    from->top -= n;
    for (i = 0; i < n; i++) {
        setobj2s(to, to->top, from->top + i);
        to->top++;  /* stack already checked by previous 'api_check' */
    }
    lua_unlock(to);
}


/**
 * 设置或获取 Lua 状态机的 panic 函数。
 *
 * @param L Lua 状态机指针。
 * @param panicf 新的 panic 函数指针；如果为 NULL，则仅返回当前的 panic 函数。
 * @return 返回之前设置的 panic 函数指针。
 *
 * @note panic 函数在 Lua 发生不可恢复的错误时被调用。
 *       如果 panic 函数返回，Lua 会调用 `exit(EXIT_FAILURE)` 终止程序。
 */
LUA_API lua_CFunction lua_atpanic(lua_State *L, lua_CFunction panicf) {
    lua_CFunction old;
    lua_lock(L);
    old = G(L)->panic;
    G(L)->panic = panicf;
    lua_unlock(L);
    return old;
}


/**
 * 获取Lua的版本号。
 *
 * 此函数返回一个指向Lua版本号的指针。如果传入的Lua状态（lua_State）为NULL，
 * 则返回全局的静态版本号；否则返回与指定Lua状态关联的版本号。
 *
 * @param L Lua状态指针，可以为NULL。
 * @return 指向Lua版本号的指针。
 */
LUA_API const lua_Number *lua_version(lua_State *L) {
    static const lua_Number version = LUA_VERSION_NUM;
    if (L == NULL) return &version;
    else return G(L)->version;
}



/*
** basic stack manipulation
*/


/**
 * 将可接受的栈索引转换为绝对索引。
 *
 * @param L Lua状态机指针。
 * @param idx 需要转换的栈索引（可以是正数、负数或伪索引）。
 * @return 返回对应的绝对索引值。
 *
 * @note 如果索引是正数或伪索引，则直接返回；否则，计算相对于当前函数栈顶的绝对位置。
 */
/*
** convert an acceptable stack index into an absolute index
*/
LUA_API int lua_absindex(lua_State *L, int idx) {
    return (idx > 0 || ispseudo(idx))
           ? idx
           : cast_int(L->top - L->ci->func) + idx;
}


/**
 * 获取当前 Lua 栈的栈顶索引。
 *
 * @param L 指向 Lua 状态的指针。
 * @return 返回栈顶的索引值（整数）。
 *
 * 说明：
 * - 计算栈顶指针 `L->top` 与当前调用帧的起始位置 `L->ci->func + 1` 之间的差值。
 * - 差值以 `StkId` 类型为单位，因此减法操作返回的是整数个 `StkId` 的大小。
 * - 加 1 操作是基于 `StkId` 类型的大小，而非单个字节。
 */
LUA_API int lua_gettop(lua_State *L) {
    // 这里计算了两个内存地址之间的间距，两个数据类型都是StkId, 因此在做减法时，将两个地址做减法之后，得到的是整数个数据类型的大小
    // 同样，加1也不是加一个字节的地址，而是，加sizeof(StkId)个字节的大小。
    return cast_int(L->top - (L->ci->func + 1));
}

/**
 * 设置 Lua 栈的栈顶位置。
 *
 * @param L Lua 状态机指针。
 * @param idx 新的栈顶位置索引。如果为正数，表示从当前函数栈帧的起始位置偏移；
 *            如果为负数，表示从当前栈顶位置向下偏移。
 *
 * @note 如果 `idx` 为正数，函数会确保栈顶位置不超过栈的最大容量，并在必要时用 `nil` 填充栈空间。
 *       如果 `idx` 为负数，函数会检查新的栈顶位置是否合法（即不会低于当前函数栈帧的起始位置）。
 *
 * @warning 调用此函数时需确保 Lua 状态机已加锁（通过 `lua_lock`），并在操作完成后解锁（通过 `lua_unlock`）。
 */
LUA_API void lua_settop(lua_State *L, int idx)
{
    StkId func = L->ci->func;
    lua_lock(L);
    if (idx >= 0) {
        api_check(L, idx <= L->stack_last - (func + 1), "new top too large");
        while (L->top < (func + 1) + idx)
            setnilvalue(L->top++);
        L->top = (func + 1) + idx;
    } else {
        api_check(L, -(idx + 1) <= (L->top - (func + 1)), "invalid new top");
        L->top += idx + 1;  /* 'subtract' index (index is negative) */
    }
    lua_unlock(L);
}

/*
** Reverse the stack segment from 'from' to 'to'
** (auxiliary to 'lua_rotate')
*/
/**
 * 反转栈中指定范围内的元素。
 *
 * @param L Lua状态机指针。
 * @param from 反转范围的起始位置（包含）。
 * @param to 反转范围的结束位置（包含）。
 *
 * @note 该函数通过交换元素的方式实现反转，适用于栈中的连续元素。
 */
static void reverse(lua_State *L, StkId from, StkId to)
{
    for (; from < to; from++, to--)
    {
        TValue temp;
        setobj(L, &temp, from);
        setobjs2s(L, from, to);
        setobj2s(L, to, &temp);
    }
}

/*
** Let x = AB, where A is a prefix of length 'n'. Then,
** rotate x n == BA. But BA == (A^r . B^r)^r.
*/
/**
 * 旋转栈中指定范围内的元素。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中起始位置的索引（从1开始或负数表示从栈顶倒数的索引）。
 * @param n 旋转的步数：
 *          - 正数表示将前n个元素移动到末尾；
 *          - 负数表示将后|n|个元素移动到开头。
 *
 * @note 该函数会锁定Lua状态机，确保线程安全。
 * @note 如果n的绝对值超过指定范围的长度，会抛出错误。
 */
LUA_API void lua_rotate(lua_State *L, int idx, int n)
{
    StkId p, t, m;
    lua_lock(L);
    t = L->top - 1;  /* end of stack segment being rotated */
    p = index2addr(L, idx);  /* start of segment */
    api_checkstackindex(L, idx, p);
    api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'");
    m = (n >= 0 ? t - n : p - n - 1);  /* end of prefix */
    reverse(L, p, m);  /* reverse the prefix with length 'n' */
    reverse(L, m + 1, t);  /* reverse the suffix */
    reverse(L, p, t);  /* reverse the entire segment */
    lua_unlock(L);
}

/**
 * 复制栈中的值到指定位置。
 *
 * @param L Lua状态机指针。
 * @param fromidx 源值的栈索引。
 * @param toidx 目标位置的栈索引。
 *
 * @note 如果目标位置是函数的上值（upvalue），则会触发垃圾回收屏障（GC barrier）。
 * @note 对于注册表索引（LUA_REGISTRYINDEX），不需要垃圾回收屏障，因为收集器会在完成回收前重新访问它。
 */
LUA_API void lua_copy(lua_State *L, int fromidx, int toidx)
{
    TValue *fr, *to;
    lua_lock(L);
    fr = index2addr(L, fromidx);
    to = index2addr(L, toidx);
    api_checkvalidindex(L, to);
    setobj(L, to, fr);
    if (isupvalue(toidx))  /* function upvalue? */
        luaC_barrier(L, clCvalue(L->ci->func), fr);
    /* LUA_REGISTRYINDEX does not need gc barrier
       (collector revisits it before finishing collection) */
    lua_unlock(L);
}

/**
 * 将指定索引处的值压入栈顶。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中值的索引（可以是负数，表示从栈顶开始计数）。
 *
 * @note 此函数是Lua API的一部分，用于操作Lua栈。
 *       调用后，栈顶会增加一个元素。
 */
LUA_API void lua_pushvalue(lua_State *L, int idx)
{
    lua_lock(L);
    setobj2s(L, L->top, index2addr(L, idx));
    api_incr_top(L);
    lua_unlock(L);
}

/*
** access functions (stack -> C)
*/

/**
 * 获取指定索引处的值的类型。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中的索引（从1开始或负数表示从栈顶开始）。
 * @return 返回值的类型（LUA_TNONE表示无效索引）。
 */
LUA_API int lua_type(lua_State *L, int idx)
{
    StkId o = index2addr(L, idx);
    return (isvalid(o) ? ttnov(o) : LUA_TNONE);
}

/**
 * 返回给定类型标签对应的类型名称字符串。
 *
 * @param L Lua状态机（未使用，仅用于API检查）。
 * @param t 类型标签，必须是有效的Lua类型标签（LUA_TNONE到LUA_NUMTAGS-1之间）。
 * @return 返回类型名称的字符串指针。如果类型标签无效，会触发API检查错误。
 */
LUA_API const char *lua_typename(lua_State *L, int t)
{
    UNUSED(L);
    api_check(L, LUA_TNONE <= t && t < LUA_NUMTAGS, "invalid tag");
    return ttypename(t);
}

/**
 * 检查指定索引处的值是否为C函数或C闭包。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中要检查的值的索引。
 * @return 如果值为C函数或C闭包，返回1；否则返回0。
 */
LUA_API int lua_iscfunction(lua_State *L, int idx)
{
    StkId o = index2addr(L, idx);
    return (ttislcf(o) || (ttisCclosure(o)));
}

/**
 * 检查指定索引处的值是否为整数类型。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中值的索引。
 * @return 如果值为整数类型返回1，否则返回0。
 */
LUA_API int lua_isinteger(lua_State *L, int idx)
{
    StkId o = index2addr(L, idx);
    return ttisinteger(o);
}

/**
 * 检查指定索引处的值是否可以转换为数字。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中值的索引。
 * @return 如果值可以转换为数字，则返回1；否则返回0。
 *
 * @note 此函数不会修改栈的内容。
 */
LUA_API int lua_isnumber(lua_State *L, int idx)
{
    lua_Number n;
    const TValue *o = index2addr(L, idx);
    return tonumber(o, &n);
}

/**
 * 检查指定索引处的值是否为字符串或可转换为字符串。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中要检查的值的索引。
 * @return 如果值为字符串或可转换为字符串，则返回1；否则返回0。
 */
LUA_API int lua_isstring(lua_State *L, int idx)
{
    const TValue *o = index2addr(L, idx);
    return (ttisstring(o) || cvt2str(o));
}

/**
 * 检查指定索引处的值是否为用户数据。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中要检查的值的索引。
 * @return 如果值为用户数据（完整用户数据或轻量用户数据），则返回1；否则返回0。
 */
LUA_API int lua_isuserdata(lua_State *L, int idx)
{
    const TValue *o = index2addr(L, idx);
    return (ttisfulluserdata(o) || ttislightuserdata(o));
}

/**
 * 比较两个 Lua 栈中的值是否严格相等（不调用元方法）。
 *
 * @param L Lua 状态机指针。
 * @param index1 第一个值的栈索引。
 * @param index2 第二个值的栈索引。
 * @return 如果两个值严格相等则返回 1，否则返回 0。
 *
 * @note 此函数不会触发任何元方法，仅进行原始值的比较。
 *       如果任一索引无效（例如超出栈范围），则返回 0。
 */
LUA_API int lua_rawequal(lua_State *L, int index1, int index2)
{
    StkId o1 = index2addr(L, index1);
    StkId o2 = index2addr(L, index2);
    return (isvalid(o1) && isvalid(o2)) ? luaV_rawequalobj(o1, o2) : 0;
}

/**
 * 对栈顶的两个元素执行算术或位运算。
 *
 * @param L Lua状态机指针。
 * @param op 操作类型（如加法、减法等），由LUA_OP*宏定义。
 *
 * @note 对于一元操作（如取负、按位取反），会自动添加一个伪操作数。
 * @note 操作结果会替换栈顶的第一个操作数，并移除第二个操作数。
 * @warning 调用此函数前需确保栈中有足够的操作数。
 */
LUA_API void lua_arith(lua_State *L, int op)
{
    lua_lock(L);
    if (op != LUA_OPUNM && op != LUA_OPBNOT)
        api_checknelems(L, 2);  /* all other operations expect two operands */
    else {  /* for unary operations, add fake 2nd operand */
        api_checknelems(L, 1);
        setobjs2s(L, L->top, L->top - 1);
        api_incr_top(L);
    }
    /* first operand at top - 2, second at top - 1; result go to top - 2 */
    luaO_arith(L, op, L->top - 2, L->top - 1, L->top - 2);
    L->top--;  /* remove second operand */
    lua_unlock(L);
}

/**
 * 比较两个 Lua 栈中的值。
 *
 * @param L Lua 状态机指针。
 * @param index1 第一个值的栈索引。
 * @param index2 第二个值的栈索引。
 * @param op 比较操作符，可以是以下之一：
 *   - LUA_OPEQ: 检查是否相等。
 *   - LUA_OPLT: 检查是否小于。
 *   - LUA_OPLE: 检查是否小于或等于。
 * @return 返回比较结果（1 表示真，0 表示假）。
 * @note 此函数会调用相应的元方法（如 `__eq`、`__lt` 或 `__le`）来完成比较。
 *       如果操作符无效，会触发 API 检查错误。
 */
LUA_API int lua_compare(lua_State *L, int index1, int index2, int op)
{
    StkId o1, o2;
    int i = 0;
    lua_lock(L);  /* may call tag method */
    o1 = index2addr(L, index1);
    o2 = index2addr(L, index2);
    if (isvalid(o1) && isvalid(o2)) {
        switch (op) {
            case LUA_OPEQ:
                i = luaV_equalobj(L, o1, o2);
                break;
            case LUA_OPLT:
                i = luaV_lessthan(L, o1, o2);
                break;
            case LUA_OPLE:
                i = luaV_lessequal(L, o1, o2);
                break;
            default:
                api_check(L, 0, "invalid option");
        }
    }
    lua_unlock(L);
    return i;
}

/**
 * 将字符串转换为数字并压入栈顶。
 *
 * @param L Lua状态机指针。
 * @param s 需要转换的字符串。
 * @return 返回转换成功的字符数量；如果转换失败，返回0。
 *
 * @note 如果转换成功，栈顶会自动增加以容纳新压入的数字。
 */
LUA_API size_t lua_stringtonumber(lua_State *L, const char *s)
{
    size_t sz = luaO_str2num(s, L->top);
    if (sz != 0)
        api_incr_top(L);
    return sz;
}

/**
 * 将指定索引处的 Lua 值转换为 lua_Number 类型。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中值的索引。
 * @param pisnum 可选参数，用于返回转换是否成功的标志（1 表示成功，0 表示失败）。
 * @return 转换后的数值。如果转换失败，返回 0。
 *
 * @note 即使转换失败，`tonumber` 函数仍可能修改返回值 `n`，因此需要显式设置为 0。
 */
LUA_API lua_Number lua_tonumberx(lua_State *L, int idx, int *pisnum)
{
    lua_Number n;
    const TValue *o = index2addr(L, idx);
    int isnum = tonumber(o, &n);
    if (!isnum)
        n = 0;  /* call to 'tonumber' may change 'n' even if it fails */
    if (pisnum) *pisnum = isnum;
    return n;
}

/**
 * 将指定索引处的 Lua 值转换为 lua_Integer 类型。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中值的索引。
 * @param pisnum 可选参数，用于返回转换是否成功的标志（非零表示成功）。
 * @return 转换后的整数值。如果转换失败，返回 0。
 *
 * @note 即使转换失败，`tointeger` 函数仍可能修改 `res` 的值。
 */
LUA_API lua_Integer lua_tointegerx(lua_State *L, int idx, int *pisnum)
{
    lua_Integer res;
    const TValue *o = index2addr(L, idx);
    int isnum = tointeger(o, &res);
    if (!isnum)
        res = 0;  /* call to 'tointeger' may change 'n' even if it fails */
    if (pisnum) *pisnum = isnum;
    return res;
}

/**
 * 将指定索引处的 Lua 值转换为布尔值。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中值的索引。
 * @return 如果值为 `nil` 或 `false`，返回 0；否则返回 1。
 *
 * @note 此函数是 Lua C API 的一部分，通常用于检查 Lua 栈中的值是否为真。
 */
LUA_API int lua_toboolean(lua_State *L, int idx)
{
    const TValue *o = index2addr(L, idx);
    return !l_isfalse(o);
}

/**
 * 将指定索引处的 Lua 值转换为字符串。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中值的索引。
 * @param len 可选参数，用于存储字符串的长度。如果为 NULL，则忽略长度。
 * @return 返回指向字符串的指针。如果值无法转换为字符串，则返回 NULL。
 *
 * @note 如果值不是字符串类型，函数会尝试将其转换为字符串。
 *       转换过程中可能会触发垃圾回收（GC）。
 *       如果转换失败，且 len 不为 NULL，则会将 len 设置为 0。
 */
LUA_API const char *lua_tolstring(lua_State *L, int idx, size_t *len)
{
    StkId o = index2addr(L, idx);
    if (!ttisstring(o))
    {
        if (!cvt2str(o)) {  /* not convertible? */
            if (len != NULL) *len = 0;
            return NULL;
        }
        lua_lock(L);  /* 'luaO_tostring' may create a new string */
        luaO_tostring(L, o);
        luaC_checkGC(L);
        o = index2addr(L, idx);  /* previous call may reallocate the stack */
        lua_unlock(L);
    }
    if (len != NULL)
        *len = vslen(o);
    return svalue(o);
}

/**
 * 获取指定索引处值的原始长度（不调用元方法）。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中值的索引。
 * @return 返回值的原始长度：
 *   - 对于短字符串（LUA_TSHRSTR），返回字符串的实际长度。
 *   - 对于长字符串（LUA_TLNGSTR），返回字符串的实际长度。
 *   - 对于用户数据（LUA_TUSERDATA），返回用户数据的长度。
 *   - 对于表（LUA_TTABLE），返回表的逻辑长度（通过`luaH_getn`计算）。
 *   - 对于其他类型，返回0。
 *
 * @note 此函数不会触发任何元方法，直接返回值的原始长度。
 */
LUA_API size_t lua_rawlen(lua_State *L, int idx)
{
    StkId o = index2addr(L, idx);
    switch (ttype(o))
    {
    case LUA_TSHRSTR:
        return tsvalue(o)->shrlen;
    case LUA_TLNGSTR:
        return tsvalue(o)->u.lnglen;
    case LUA_TUSERDATA:
        return uvalue(o)->len;
    case LUA_TTABLE:
        return luaH_getn(hvalue(o));
    default:
        return 0;
    }
}

/**
 * 从 Lua 栈中获取指定索引处的 C 函数。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中的索引位置。
 * @return 返回指向 C 函数的指针；如果索引处的值不是 C 函数，则返回 NULL。
 *
 * @note 该函数用于检查栈中的值是否为 C 函数，并返回其指针。
 *       如果值是轻量级 C 函数（light C function），直接返回其值；
 *       如果值是 C 闭包（C closure），返回其函数指针；
 *       否则返回 NULL。
 */
LUA_API lua_CFunction lua_tocfunction(lua_State *L, int idx)
{
    StkId o = index2addr(L, idx);
    if (ttislcf(o))
        return fvalue(o);
    else if (ttisCclosure(o))
        return clCvalue(o)->f;
    else return NULL;  /* not a C function */
}

/**
 * 从 Lua 栈中获取指定索引处的用户数据。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中的索引位置。
 * @return 如果索引处的值为用户数据（LUA_TUSERDATA），则返回其内存指针；
 *         如果为轻量用户数据（LUA_TLIGHTUSERDATA），则直接返回其指针；
 *         否则返回 NULL。
 */
LUA_API void *lua_touserdata(lua_State *L, int idx)
{
    StkId o = index2addr(L, idx);
    switch (ttnov(o))
    {
    case LUA_TUSERDATA:
        return getudatamem(uvalue(o));
    case LUA_TLIGHTUSERDATA:
        return pvalue(o);
    default:
        return NULL;
    }
}

/**
 * 将指定索引处的值转换为 Lua 线程（协程）对象。
 * 如果该值不是线程类型，则返回 NULL。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中值的索引。
 * @return 如果成功转换，返回对应的 Lua 线程对象；否则返回 NULL。
 */
LUA_API lua_State *lua_tothread(lua_State *L, int idx)
{
    StkId o = index2addr(L, idx);
    return (!ttisthread(o)) ? NULL : thvalue(o);
}

/**
 * 获取指定索引处的 Lua 值的指针表示。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中值的索引。
 * @return 返回指向 Lua 值的指针，具体类型取决于值的类型：
 *   - 表 (LUA_TTABLE): 返回表的指针。
 *   - Lua 闭包 (LUA_TLCL): 返回闭包的指针。
 *   - C 闭包 (LUA_TCCL): 返回 C 闭包的指针。
 *   - C 函数 (LUA_TLCF): 返回函数的指针。
 *   - 线程 (LUA_TTHREAD): 返回线程的指针。lua_State 也是线程。
 *   - 用户数据 (LUA_TUSERDATA): 返回用户数据的内存指针。
 *   - 轻量用户数据 (LUA_TLIGHTUSERDATA): 返回轻量用户数据的指针。
 *   - 其他类型: 返回 NULL。
 */
LUA_API const void *lua_topointer(lua_State *L, int idx)
{
    StkId o = index2addr(L, idx);
    switch (ttype(o))
    {
    case LUA_TTABLE:
        return hvalue(o);
    case LUA_TLCL:
        return clLvalue(o);
    case LUA_TCCL:
        return clCvalue(o);
    case LUA_TLCF:
        return cast(void *, cast(size_t, fvalue(o)));
    case LUA_TTHREAD:
        return thvalue(o);
    case LUA_TUSERDATA:
        return getudatamem(uvalue(o));
    case LUA_TLIGHTUSERDATA:
        return pvalue(o);
    default:
        return NULL;
    }
}

/*
** push functions (C -> stack)
*/

/**
 * 将一个 nil 值压入 Lua 栈顶。
 *
 * @param L 指向 Lua 状态的指针。
 * @note 此函数是线程安全的，调用时会自动加锁和解锁。
 */
LUA_API void lua_pushnil(lua_State *L)
{
    lua_lock(L);
    setnilvalue(L->top);
    api_incr_top(L);
    lua_unlock(L);
}

/**
 * 将一个数值压入 Lua 栈顶。
 *
 * @param L Lua 状态机指针。
 * @param n 要压入栈的数值。
 *
 * @note 此函数是线程安全的，内部会加锁保护栈操作。
 */
LUA_API void lua_pushnumber(lua_State *L, lua_Number n)
{
    lua_lock(L);
    setfltvalue(L->top, n);
    api_incr_top(L);
    lua_unlock(L);
}

/**
 * 将一个整数值压入 Lua 栈顶。
 *
 * @param L Lua 状态机指针。
 * @param n 要压入的整数值。
 *
 * @note 此函数是 Lua API 的一部分，用于在 Lua 栈顶添加一个整数值。
 *       调用时会自动加锁以确保线程安全。
 */
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
{
    lua_lock(L);
    setivalue(L->top, n);
    api_incr_top(L);
    lua_unlock(L);
}

/*
** Pushes on the stack a string with given length. Avoid using 's' when
** 'len' == 0 (as 's' can be NULL in that case), due to later use of
** 'memcmp' and 'memcpy'.
*/
/**
 * 将一个指定长度的字符串压入 Lua 栈顶。
 *
 * @param L Lua 状态机指针。
 * @param s 要压入的字符串的起始地址。
 * @param len 字符串的长度（字节数）。
 * @return 返回压入的字符串的内部表示（const char* 类型）。
 *
 * @note 如果字符串长度为 0，则压入空字符串 ""。
 * @note 此函数会触发垃圾回收检查（luaC_checkGC）。
 * @note 函数内部会加锁（lua_lock）和解锁（lua_unlock），确保线程安全。
 */
LUA_API const char *lua_pushlstring(lua_State *L, const char *s, size_t len)
{
    TString *ts;
    lua_lock(L);
    ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
    setsvalue2s(L, L->top, ts);
    api_incr_top(L);
    luaC_checkGC(L);
    lua_unlock(L);
    return getstr(ts);
}

/**
 * 将一个C字符串压入Lua栈中。
 *
 * @param L Lua状态机指针。
 * @param s 要压入的C字符串。如果为NULL，则压入nil。
 * @return 返回内部字符串的地址（如果成功压入字符串），否则返回NULL。
 *
 * @note 此函数是Lua API的一部分，线程安全且会触发垃圾回收检查。
 *       如果字符串为NULL，栈顶将被设置为nil；否则，字符串会被复制到Lua内部并压入栈顶。
 */
LUA_API const char *lua_pushstring(lua_State *L, const char *s)
{
    lua_lock(L);
    if (s == NULL)
        setnilvalue(L->top);
    else {
        TString *ts;
        ts = luaS_new(L, s);
        setsvalue2s(L, L->top, ts);
        s = getstr(ts);  /* internal copy's address */
    }
    api_incr_top(L);
    luaC_checkGC(L);
    lua_unlock(L);
    return s;
}

/**
 * 将一个格式化字符串及其可变参数推入 Lua 栈中。
 *
 * @param L Lua 状态机指针。
 * @param fmt 格式化字符串，类似于 printf 的格式。
 * @param argp 可变参数列表，用于填充格式化字符串。
 * @return 返回推入栈中的字符串的指针。
 *
 * @note 此函数会锁定 Lua 状态机，确保线程安全。
 * @note 在推入字符串后，会触发垃圾回收检查。
 */
LUA_API const char *lua_pushvfstring(lua_State *L, const char *fmt,
                                     va_list argp)
{
    const char *ret;
    lua_lock(L);
    ret = luaO_pushvfstring(L, fmt, argp);
    luaC_checkGC(L);
    lua_unlock(L);
    return ret;
}

/**
 * 将格式化字符串压入 Lua 栈中。
 *
 * 此函数类似于 `sprintf`，但直接将结果字符串压入 Lua 栈中，并返回该字符串的指针。
 * 支持可变参数，格式字符串的语法与标准 C 库的 `printf` 类似。
 *
 * @param L Lua 状态机指针。
 * @param fmt 格式化字符串，用于指定输出的格式。
 * @param ... 可变参数，根据 `fmt` 的格式要求提供。
 * @return 返回压入栈中的字符串的指针。
 *
 * @note 此函数是线程安全的，会调用 `lua_lock` 和 `lua_unlock` 进行同步。
 * @note 函数内部会触发垃圾回收检查 (`luaC_checkGC`)。
 */
LUA_API const char *lua_pushfstring(lua_State *L, const char *fmt, ...)
{
    const char *ret;
    va_list argp;
    lua_lock(L);
    va_start(argp, fmt);
    ret = luaO_pushvfstring(L, fmt, argp);
    va_end(argp);
    luaC_checkGC(L);
    lua_unlock(L);
    return ret;
}

/**
 * 将一个 C 闭包压入 Lua 栈。
 *
 * @param L Lua 状态机指针。
 * @param fn C 函数指针，作为闭包的主体。
 * @param n 闭包捕获的上值（upvalue）数量。
 *
 * @note 如果 n 为 0，直接将 C 函数压入栈顶；
 *       否则，创建一个新的 C 闭包，并将上值从栈中复制到闭包中。
 * @warning 上值数量不能超过 MAXUPVAL，否则会触发错误。
 *
 * 该函数是 Lua API 的一部分，通常用于将 C 函数封装为 Lua 可调用的闭包。
 */
LUA_API void lua_pushcclosure(lua_State *L, lua_CFunction fn, int n)
{
    lua_lock(L);
    if (n == 0)
    {
        setfvalue(L->top, fn);
        api_incr_top(L);
    }
    else
    {
        CClosure *cl;
        api_checknelems(L, n);
        api_check(L, n <= MAXUPVAL, "upvalue index too large");
        cl = luaF_newCclosure(L, n);
        cl->f = fn;
        L->top -= n;
        while (n--) {
            setobj2n(L, &cl->upvalue[n], L->top + n);
            /* does not need barrier because closure is white */
        }
        setclCvalue(L, L->top, cl);
        api_incr_top(L);
        luaC_checkGC(L);
    }
    lua_unlock(L);
}

/**
 * 将一个布尔值压入 Lua 栈顶。
 *
 * @param L Lua 状态机指针。
 * @param b 布尔值（非零为 true，零为 false）。
 *
 * @note 此函数是 Lua C API 的一部分，用于在 C 代码中与 Lua 交互。
 *       调用后，栈顶会增加一个布尔值。
 */
LUA_API void lua_pushboolean(lua_State *L, int b)
{
    lua_lock(L);
    setbvalue(L->top, (b != 0)); /* ensure that true is 1 */
    api_incr_top(L);
    lua_unlock(L);
}

/**
 * 将一个轻量级用户数据压入 Lua 栈顶。
 *
 * @param L Lua 状态机指针。
 * @param p 指向用户数据的指针。
 *
 * @note 此函数是线程安全的，会通过锁机制保护 Lua 栈的操作。
 * @note 轻量级用户数据是 Lua 中不管理内存的指针，通常用于与 C 代码交互。
 */
LUA_API void lua_pushlightuserdata(lua_State *L, void *p)
{
    lua_lock(L);
    setpvalue(L->top, p);
    api_incr_top(L);
    lua_unlock(L);
}

/**
 * 将当前线程（协程）压入栈顶。
 *
 * @param L Lua状态机指针。
 * @return 如果当前线程是主线程，则返回1；否则返回0。
 *
 * 此函数是Lua API的一部分，用于在多线程环境中管理协程。
 * 调用后，栈顶将包含当前线程的引用。
 */
LUA_API int lua_pushthread(lua_State *L)
{
    lua_lock(L);
    setthvalue(L, L->top, L);
    api_incr_top(L);
    lua_unlock(L);
    return (G(L)->mainthread == L);
}

/*
** get functions (Lua -> stack)
*/

/**
 * 从表 `t` 中获取键为 `k` 的字符串值。
 * 如果键存在，则将对应的值压入栈顶并返回其类型；如果键不存在，则将键名作为字符串压入栈顶并尝试获取其值。
 *
 * @param L Lua 状态机指针。
 * @param t 指向表的指针。
 * @param k 要查找的键名（字符串）。
 * @return 返回栈顶值的类型（通过 `ttnov` 解析）。
 *
 * @note 此函数是内部实现细节，通常不直接暴露给用户。
 */
static int auxgetstr(lua_State *L, const TValue *t, const char *k)
{
    const TValue *slot;
    TString *str = luaS_new(L, k);
    if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
        setobj2s(L, L->top, slot);
        api_incr_top(L);
    } else {
        setsvalue2s(L, L->top, str);
        api_incr_top(L);
        luaV_finishget(L, t, L->top - 1, L->top - 1, slot);
    }
    lua_unlock(L);
    return ttnov(L->top - 1);
}

/**
 * 获取全局变量的值并将其压入栈顶。
 *
 * @param L Lua状态机指针。
 * @param name 全局变量的名称。
 * @return 返回1，表示成功将全局变量的值压入栈顶。
 *
 * @note 此函数是Lua API的一部分，用于从全局表中获取指定名称的变量。
 *       调用此函数会锁定Lua状态机，确保线程安全。
 */
LUA_API int lua_getglobal(lua_State *L, const char *name)
{
    Table *reg = hvalue(&G(L)->l_registry);
    lua_lock(L);
    return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
}

/**
 * 从 Lua 栈中获取指定索引处的表，并根据栈顶的键获取对应的值。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中表的索引（可以是绝对索引或相对索引）。
 * @return 返回值的类型（通过 `ttnov` 宏获取）。
 *
 * @note 此函数是 Lua C API 的一部分，用于操作 Lua 栈中的表数据。
 *       调用时需确保栈顶是一个有效的键，且索引 `idx` 指向一个表。
 *       函数会修改栈顶内容，将键替换为对应的值。
 */
LUA_API int lua_gettable(lua_State *L, int idx)
{
    StkId t;
    lua_lock(L);
    t = index2addr(L, idx);
    luaV_gettable(L, t, L->top - 1, L->top - 1);
    lua_unlock(L);
    return ttnov(L->top - 1);
}

/**
 * 获取表中指定键的值并将其压入栈顶。
 *
 * @param L Lua状态机指针。
 * @param idx 表的栈索引。
 * @param k 键名（字符串）。
 * @return 返回值的类型（Lua类型常量）。
 *
 * @note 此函数是Lua C API的一部分，用于从表中获取字段值。
 *       调用前需确保栈中有足够的空间。
 */
LUA_API int lua_getfield(lua_State *L, int idx, const char *k)
{
    lua_lock(L);
    return auxgetstr(L, index2addr(L, idx), k);
}

/**
 * 从 Lua 栈中获取指定索引处的表（或类似表的对象）的整型键对应的值。
 *
 * @param L Lua 状态机指针。
 * @param idx 栈中表的索引。
 * @param n 整型键。
 * @return 返回值的类型（通过 `ttnov` 宏获取）。
 *
 * @note 此函数是 Lua API 的一部分，用于高效地从表中获取整型键对应的值。
 *       如果键存在，则直接返回值；否则，会触发元方法调用。
 *       调用此函数会修改栈顶。
 */
LUA_API int lua_geti(lua_State *L, int idx, lua_Integer n)
{
    StkId t;
    const TValue *slot;
    lua_lock(L);
    t = index2addr(L, idx);
    if (luaV_fastget(L, t, n, slot, luaH_getint)) {
        setobj2s(L, L->top, slot);
        api_incr_top(L);
    } else {
        setivalue(L->top, n);
        api_incr_top(L);
        luaV_finishget(L, t, L->top - 1, L->top - 1, slot);
    }
    lua_unlock(L);
    return ttnov(L->top - 1);
}

/**
 * 从表中获取指定索引处的原始值（不触发元方法）。
 *
 * @param L Lua 状态机指针。
 * @param idx 表的索引（栈中的位置）。
 * @return 返回值的类型（Lua 类型常量）。
 *
 * @note 此函数不会调用元方法，直接访问表的原始数据。
 *       调用者需确保 `idx` 指向的是一个表。
 */
LUA_API int lua_rawget(lua_State *L, int idx)
{
    StkId t;
    lua_lock(L);
    t = index2addr(L, idx);
    api_check(L, ttistable(t), "table expected");
    setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1));
    lua_unlock(L);
    return ttnov(L->top - 1);
}

/**
 * 从表中获取指定索引的原始值（不触发元方法）。
 *
 * @param L Lua状态机指针。
 * @param idx 表的栈索引。
 * @param n 表中要获取的整数索引。
 * @return 返回值的类型（LUA_TNIL、LUA_TBOOLEAN等），不压入栈。
 *
 * @note 此函数是Lua C API的一部分，用于直接操作Lua栈和表。
 *       与`lua_geti`不同，此函数不会调用元方法。
 */
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
{
    StkId t;
    lua_lock(L);
    t = index2addr(L, idx);
    api_check(L, ttistable(t), "table expected");
    setobj2s(L, L->top, luaH_getint(hvalue(t), n));
    api_incr_top(L);
    lua_unlock(L);
    return ttnov(L->top - 1);
}

/**
 * 从表中获取与指针键 `p` 关联的值，不触发元方法。
 *
 * @param L Lua 状态机指针。
 * @param idx 表的栈索引。
 * @param p 用作键的指针。
 * @return 返回值的类型（Lua 类型常量），如果键不存在则返回 `LUA_TNIL`。
 *
 * @note 此函数是原始操作，不会调用任何元方法。
 * @warning 调用者需确保 `idx` 是一个有效的表。
 */
LUA_API int lua_rawgetp(lua_State *L, int idx, const void *p)
{
    StkId t;
    TValue k;
    lua_lock(L);
    t = index2addr(L, idx);
    api_check(L, ttistable(t), "table expected");
    setpvalue(&k, cast(void *, p));
    setobj2s(L, L->top, luaH_get(hvalue(t), &k));
    api_incr_top(L);
    lua_unlock(L);
    return ttnov(L->top - 1);
}

/**
 * 创建一个新的 Lua 表并将其压入栈顶。
 *
 * @param L Lua 状态机指针。
 * @param narray 表的数组部分初始容量（建议大小）。
 * @param nrec 表的哈希部分初始容量（建议大小）。
 *
 * @note 如果 `narray` 或 `nrec` 大于 0，表会根据指定大小进行预分配以提高性能。
 * @note 此函数是 Lua API 的一部分，通常用于在 Lua 中动态创建表结构。
 */
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
{
    Table *t;
    lua_lock(L);
    t = luaH_new(L);
    sethvalue(L, L->top, t);
    api_incr_top(L);
    if (narray > 0 || nrec > 0)
        luaH_resize(L, t, narray, nrec);
    luaC_checkGC(L);
    lua_unlock(L);
}

/**
 * @brief 获取指定索引处对象的元表。
 *
 * 该函数用于获取 Lua 栈中指定索引处对象的元表。如果对象有元表，则将其压入栈顶并返回 1；
 * 如果没有元表，则返回 0。
 *
 * @param L Lua 状态机指针。
 * @param objindex 栈中对象的索引。
 * @return 如果成功获取元表并压入栈顶，返回 1；否则返回 0。
 *
 * @note 该函数是 Lua C API 的一部分，通常用于扩展 Lua 或与 Lua 交互的 C 代码中。
 *       支持的对象的类型包括表（LUA_TTABLE）、用户数据（LUA_TUSERDATA）以及其他基本类型。
 */
LUA_API int lua_getmetatable(lua_State *L, int objindex)
{
    const TValue *obj;
    Table *mt;
    int res = 0;
    lua_lock(L);
    obj = index2addr(L, objindex);
    switch (ttnov(obj)) {
        case LUA_TTABLE:
            mt = hvalue(obj)->metatable;
            break;
        case LUA_TUSERDATA:
            mt = uvalue(obj)->metatable;
            break;
        default:
            mt = G(L)->mt[ttnov(obj)];
            break;
    }
    if (mt != NULL) {
        sethvalue(L, L->top, mt);
        api_incr_top(L);
        res = 1;
    }
    lua_unlock(L);
    return res;
}

/**
 * 获取指定索引处的用户数据的关联值。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中用户数据的索引。
 * @return 返回关联值的类型（通过 `ttnov` 转换后的结果）。
 *
 * @note 此函数要求索引处的值必须是一个完整的用户数据（full userdata），否则会触发错误。
 * @note 函数会锁定Lua状态机，操作完成后解锁。
 */
LUA_API int lua_getuservalue(lua_State *L, int idx)
{
    StkId o;
    lua_lock(L);
    o = index2addr(L, idx);
    api_check(L, ttisfulluserdata(o), "full userdata expected");
    getuservalue(L, uvalue(o), L->top);
    api_incr_top(L);
    lua_unlock(L);
    return ttnov(L->top - 1);
}

/*
** set functions (stack -> Lua)
*/

/*
** t[k] = value at the top of the stack (where 'k' is a string)
*/
/**
 * 在 Lua 表中设置字符串键对应的值。
 *
 * @param L Lua 状态机指针。
 * @param t 指向表的 TValue 指针。
 * @param k 要设置的字符串键。
 *
 * 功能说明：
 * - 创建一个新的字符串键 `k` 并将其与栈顶的值关联到表 `t` 中。
 * - 如果快速设置成功（通过 `luaV_fastset`），则弹出栈顶的值。
 * - 否则，将字符串键和值压入栈，并通过 `luaV_finishset` 完成设置，最后弹出键和值。
 * - 调用者负责加锁，函数结束时解锁。
 *
 * 注意：
 * - 该函数是静态的，仅供内部使用。
 * - 调用者需确保栈顶的值是有效的。
 */
static void auxsetstr(lua_State *L, const TValue *t, const char *k)
{
    const TValue *slot;
    TString *str = luaS_new(L, k);
    api_checknelems(L, 1);
    if (luaV_fastset(L, t, str, slot, luaH_getstr, L->top - 1))
        L->top--;  /* pop value */
    else {
        setsvalue2s(L, L->top, str);  /* push 'str' (to make it a TValue) */
        api_incr_top(L);
        luaV_finishset(L, t, L->top - 1, L->top - 2, slot);
        L->top -= 2;  /* pop value and key */
    }
    lua_unlock(L);  /* lock done by caller */
}

/**
 * 设置全局变量的值。
 *
 * @param L Lua状态机指针。
 * @param name 全局变量的名称。
 *
 * @note 此函数将全局变量 `name` 的值设置为栈顶的值，并弹出该值。
 *       如果全局变量不存在，则会创建它。
 *       函数内部会锁定状态机，解锁操作由 `auxsetstr` 完成。
 */
LUA_API void lua_setglobal(lua_State *L, const char *name)
{
    Table *reg = hvalue(&G(L)->l_registry);
    lua_lock(L); /* unlock done in 'auxsetstr' */
    auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
}

/**
 * 设置表中指定索引处的值。
 *
 * @param L Lua状态机指针。
 * @param idx 表中要设置的索引。
 *
 * 功能：
 * - 从栈顶弹出两个元素（键和值），并将它们设置到表中指定索引的位置。
 * - 操作完成后，栈顶的两个元素（键和值）会被弹出。
 *
 * 注意：
 * - 调用此函数前，必须确保栈顶有两个元素（键和值）。
 * - 此函数是Lua API的一部分，通常用于C代码与Lua交互。
 */
LUA_API void lua_settable(lua_State *L, int idx)
{
    StkId t;
    lua_lock(L);
    api_checknelems(L, 2);
    t = index2addr(L, idx);
    luaV_settable(L, t, L->top - 2, L->top - 1);
    L->top -= 2;  /* pop index and value */
    lua_unlock(L);
}

/**
 * 设置表中指定键的值。
 *
 * @param L Lua状态机指针。
 * @param idx 表的栈索引。
 * @param k 键名（字符串）。
 *
 * @note 此函数会锁定Lua状态机，解锁操作由内部函数`auxsetstr`完成。
 */
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
{
    lua_lock(L); /* unlock done in 'auxsetstr' */
    auxsetstr(L, index2addr(L, idx), k);
}

/**
 * 设置表中指定索引处的值。
 *
 * @param L Lua 状态机指针。
 * @param idx 表的栈索引。
 * @param n 要设置的整数索引。
 *
 * @note 此函数是 Lua API 的一部分，用于操作表中的值。
 *       如果表支持快速设置（如数组部分），则直接设置值；
 *       否则，通过通用路径完成设置操作。
 *       调用后，栈顶的值会被弹出。
 */
LUA_API void lua_seti(lua_State *L, int idx, lua_Integer n)
{
    StkId t;
    const TValue *slot;
    lua_lock(L);
    api_checknelems(L, 1);
    t = index2addr(L, idx);
    if (luaV_fastset(L, t, n, slot, luaH_getint, L->top - 1))
        L->top--;  /* pop value */
    else {
        setivalue(L->top, n);
        api_incr_top(L);
        luaV_finishset(L, t, L->top - 1, L->top - 2, slot);
        L->top -= 2;  /* pop value and key */
    }
    lua_unlock(L);
}

/**
 * 设置指定索引处的表项，不触发元方法。
 *
 * @param L Lua 状态机指针。
 * @param idx 表的索引。
 *
 * @note 此函数是 Lua 的原始 API 函数，直接操作表项而不调用任何元方法。
 *       调用者需确保栈顶有两个元素（键和值），且索引 `idx` 处是一个表。
 *       函数会锁定状态机，执行操作后解锁。
 */
LUA_API void lua_rawset(lua_State *L, int idx)
{
    StkId o;
    TValue *slot;
    lua_lock(L);
    api_checknelems(L, 2);
    o = index2addr(L, idx);
    api_check(L, ttistable(o), "table expected");
    slot = luaH_set(L, hvalue(o), L->top - 2);
    setobj2t(L, slot, L->top - 1);
    invalidateTMcache(hvalue(o));
    luaC_barrierback(L, hvalue(o), L->top - 1);
    L->top -= 2;
    lua_unlock(L);
}

/**
 * 设置表中指定索引的值（不触发元方法）。
 *
 * @param L Lua 状态机指针。
 * @param idx 表的栈索引。
 * @param n 表中要设置的整数索引。
 *
 * @note 此函数直接操作表，不会触发任何元方法（如 `__newindex`）。
 * @warning 调用此函数前，必须确保栈顶元素是要设置的值，且 `idx` 指向的是一个表。
 * @see lua_seti 如果需要触发元方法，请使用 `lua_seti`。
 */
LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n)
{
    StkId o;
    lua_lock(L);
    api_checknelems(L, 1);
    o = index2addr(L, idx);
    api_check(L, ttistable(o), "table expected");
    luaH_setint(L, hvalue(o), n, L->top - 1);
    luaC_barrierback(L, hvalue(o), L->top - 1);
    L->top--;
    lua_unlock(L);
}

/**
 * 在 Lua 表中设置一个原始指针键对应的值。
 *
 * @param L Lua 状态机指针。
 * @param idx 表的栈索引。
 * @param p 用作键的原始指针。
 *
 * @note 此函数是 Lua C API 的一部分，用于直接操作 Lua 表。
 *       它不执行元方法调用，因此比 `lua_settable` 更高效。
 *       调用此函数时，栈顶应为要设置的值。
 *
 * @warning 调用者需确保 `idx` 是一个有效的表索引，且栈顶有足够的元素。
 *          此函数不会检查键或值的类型，直接进行赋值。
 */
LUA_API void lua_rawsetp(lua_State *L, int idx, const void *p)
{
    StkId o;
    TValue k, *slot;
    lua_lock(L);
    api_checknelems(L, 1);
    o = index2addr(L, idx);
    api_check(L, ttistable(o), "table expected");
    setpvalue(&k, cast(void *, p));
    slot = luaH_set(L, hvalue(o), &k);
    setobj2t(L, slot, L->top - 1);
    luaC_barrierback(L, hvalue(o), L->top - 1);
    L->top--;
    lua_unlock(L);
}

/**
 * 设置指定对象的元表。
 *
 * @param L Lua状态机指针。
 * @param objindex 对象在栈中的索引。
 * @return 始终返回1。
 *
 * @note
 * - 如果栈顶的值为nil，则清除对象的元表。
 * - 如果栈顶的值为表，则将其设置为对象的元表。
 * - 支持的对象类型包括表（LUA_TTABLE）、用户数据（LUA_TUSERDATA）和其他类型。
 * - 对于表和用户数据，设置元表时会触发垃圾回收屏障（barrier）和终结器检查。
 */
LUA_API int lua_setmetatable(lua_State *L, int objindex)
{
    TValue *obj;
    Table *mt;
    lua_lock(L);
    api_checknelems(L, 1);
    obj = index2addr(L, objindex);
    if (ttisnil(L->top - 1))
        mt = NULL;
    else {
        api_check(L, ttistable(L->top - 1), "table expected");
        mt = hvalue(L->top - 1);
    }
    switch (ttnov(obj)) {
        case LUA_TTABLE: {
            hvalue(obj)->metatable = mt;
            if (mt) {
                luaC_objbarrier(L, gcvalue(obj), mt);
                luaC_checkfinalizer(L, gcvalue(obj), mt);
            }
            break;
        }
        case LUA_TUSERDATA: {
            uvalue(obj)->metatable = mt;
            if (mt) {
                luaC_objbarrier(L, uvalue(obj), mt);
                luaC_checkfinalizer(L, gcvalue(obj), mt);
            }
            break;
        }
        default: {
            G(L)->mt[ttnov(obj)] = mt;
            break;
        }
    }
    L->top--;
    lua_unlock(L);
    return 1;
}

/**
 * 设置与指定用户数据关联的值。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中用户数据的索引。
 *
 * @note 此函数会检查栈顶元素是否为用户数据，并将其设置为与用户数据关联的值。
 *       调用后，栈顶元素会被弹出。
 * @warning 必须确保栈顶元素是完整的用户数据，否则会触发API检查错误。
 */
LUA_API void lua_setuservalue(lua_State *L, int idx)
{
    StkId o;
    lua_lock(L);
    api_checknelems(L, 1);
    o = index2addr(L, idx);
    api_check(L, ttisfulluserdata(o), "full userdata expected");
    setuservalue(L, uvalue(o), L->top - 1);
    luaC_barrier(L, gcvalue(o), L->top - 1);
    L->top--;
    lua_unlock(L);
}

/*
** 'load' and 'call' functions (run Lua code)
*/


#define checkresults(L, na, nr) \
     api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \
  "results from function overflow current stack size")

/**
 * 调用 Lua 函数，支持延续（continuation）机制。
 *
 * @param L Lua 状态机指针。
 * @param nargs 传递给函数的参数数量。
 * @param nresults 期望的返回值数量。
 * @param ctx 传递给延续函数的上下文信息。
 * @param k 延续函数（可为 NULL，表示不需要延续）。
 *
 * @note 该函数是 Lua C API 的一部分，用于在 C 代码中调用 Lua 函数。
 *       如果提供了延续函数 `k`，则在函数调用完成后会触发延续逻辑。
 *       该函数会检查线程状态和参数合法性，确保调用环境正常。
 *       调用过程中会锁定 Lua 状态机，避免并发问题。
 */
LUA_API void lua_callk(lua_State *L, int nargs, int nresults,
                       lua_KContext ctx, lua_KFunction k)
{
    StkId func;
    lua_lock(L);
    api_check(L, k == NULL || !isLua(L->ci),
              "cannot use continuations inside hooks");
    api_checknelems(L, nargs + 1);
    api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
    checkresults(L, nargs, nresults);
    func = L->top - (nargs + 1);
    if (k != NULL && L->nny == 0) {  /* need to prepare continuation? */
        L->ci->u.c.k = k;  /* save continuation */
        L->ci->u.c.ctx = ctx;  /* save context */
        luaD_call(L, func, nresults);  /* do the call */
    } else  /* no continuation or no yieldable */
        luaD_callnoyield(L, func, nresults);  /* just do the call */
    adjustresults(L, nresults);
    lua_unlock(L);
}

/*
** Execute a protected call.
*/
struct CallS {  /* data to 'f_call' */
    StkId func;
    int nresults;
};

/**
 * 调用 Lua 函数但不允许 yield。
 *
 * @param L Lua 状态机指针。
 * @param ud 用户数据，指向一个 `CallS` 结构体，包含要调用的函数和期望的结果数量。
 *
 * @note 此函数是静态的，仅供内部使用。
 * @note 调用过程中不允许 yield，适用于需要在不可中断上下文中执行 Lua 函数的场景。
 */
static void f_call(lua_State *L, void *ud)
{
    struct CallS *c = cast(struct CallS *, ud);
    luaD_callnoyield(L, c->func, c->nresults);
}

/**
 * 在受保护的环境中调用 Lua 函数，支持延续（continuation）。
 *
 * @param L Lua 状态机指针。
 * @param nargs 传递给函数的参数数量。
 * @param nresults 期望的返回值数量。
 * @param errfunc 错误处理函数在栈中的索引（0 表示无错误处理函数）。
 * @param ctx 延续上下文，传递给延续函数。
 * @param k 延续函数，用于在函数执行后继续操作（NULL 表示无延续）。
 *
 * @return 函数执行状态：
 *   - LUA_OK 表示成功。
 *   - 其他值表示错误（如 LUA_ERRRUN）。
 *
 * @note 该函数是 Lua API 的一部分，通常用于在受保护的环境中调用 Lua 函数，
 *       支持通过延续机制实现非阻塞调用。
 * @warning 不能在钩子函数中使用延续。
 */
LUA_API int lua_pcallk(lua_State *L, int nargs, int nresults, int errfunc,
                       lua_KContext ctx, lua_KFunction k)
{
    struct CallS c;
    int status;
    ptrdiff_t func;
    lua_lock(L);
    api_check(L, k == NULL || !isLua(L->ci),
              "cannot use continuations inside hooks");
    api_checknelems(L, nargs + 1);
    api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
    checkresults(L, nargs, nresults);
    if (errfunc == 0)
        func = 0;
    else {
        StkId o = index2addr(L, errfunc);
        api_checkstackindex(L, errfunc, o);
        func = savestack(L, o);
    }
    c.func = L->top - (nargs + 1);  /* function to be called */
    if (k == NULL || L->nny > 0) {  /* no continuation or no yieldable? */
        c.nresults = nresults;  /* do a 'conventional' protected call */
        status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
    } else {  /* prepare continuation (call is already protected by 'resume') */
        CallInfo *ci = L->ci;
        ci->u.c.k = k;  /* save continuation */
        ci->u.c.ctx = ctx;  /* save context */
        /* save information for error recovery */
        ci->extra = savestack(L, c.func);
        ci->u.c.old_errfunc = L->errfunc;
        L->errfunc = func;
        setoah(ci->callstatus, L->allowhook);  /* save value of 'allowhook' */
        ci->callstatus |= CIST_YPCALL;  /* function can do error recovery */
        luaD_call(L, c.func, nresults);  /* do the call */
        ci->callstatus &= ~CIST_YPCALL;
        L->errfunc = ci->u.c.old_errfunc;
        status = LUA_OK;  /* if it is here, there were no errors */
    }
    adjustresults(L, nresults);
    lua_unlock(L);
    return status;
}

/**
 * 加载并编译 Lua 代码块。
 *
 * @param L Lua 状态机指针。
 * @param reader 用于读取代码块的函数指针。
 * @param data 传递给 reader 的自定义数据。
 * @param chunkname 代码块的名称（用于调试和错误信息），如果为 NULL，则默认为 "?"。
 * @param mode 编译模式（可选），可以是 "b"（二进制）、"t"（文本）或 NULL（默认）。
 * @return 返回状态码：
 *   - LUA_OK: 成功加载和编译。
 *   - 其他错误码: 加载或编译失败。
 *
 * @note 此函数是 Lua 的核心 API 之一，用于动态加载 Lua 代码。
 *       如果加载的代码块是一个闭包且包含至少一个上值，则会将全局表设置为第一个上值。
 *       函数内部会加锁以确保线程安全。
 */
LUA_API int lua_load(lua_State *L, lua_Reader reader, void *data,
                     const char *chunkname, const char *mode)
{
    ZIO z;
    int status;
    lua_lock(L);
    if (!chunkname) chunkname = "?";
    luaZ_init(L, &z, reader, data);
    status = luaD_protectedparser(L, &z, chunkname, mode);
    if (status == LUA_OK) {  /* no errors? */
        LClosure *f = clLvalue(L->top - 1);  /* get newly created function */
        if (f->nupvalues >= 1) {  /* does it have an upvalue? */
            /* get global table from registry */
            Table *reg = hvalue(&G(L)->l_registry);
            const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
            /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
            setobj(L, f->upvals[0]->v, gt);
            luaC_upvalbarrier(L, f->upvals[0]);
        }
    }
    lua_unlock(L);
    return status;
}

/**
 * 将 Lua 函数转储为二进制块。
 *
 * @param L Lua 状态机指针。
 * @param writer 用于写入二进制数据的回调函数。
 * @param data 传递给回调函数的用户数据。
 * @param strip 是否去除调试信息（非零表示去除）。
 * @return 成功返回 0，失败返回 1。
 *
 * @note 如果栈顶元素不是 Lua 函数，则直接返回 1。
 * @warning 调用此函数前需确保栈顶是一个 Lua 函数。
 */
LUA_API int lua_dump(lua_State *L, lua_Writer writer, void *data, int strip)
{
    int status;
    TValue *o;
    lua_lock(L);
    api_checknelems(L, 1);
    o = L->top - 1;
    if (isLfunction(o))
        status = luaU_dump(L, getproto(o), writer, data, strip);
    else
        status = 1;
    lua_unlock(L);
    return status;
}

/**
 * 获取当前Lua线程的状态。
 *
 * @param L 指向Lua状态的指针。
 * @return 返回当前Lua线程的状态码。
 */
LUA_API int lua_status(lua_State *L)
{
    return L->status;
}

/*
** Garbage-collection function
*/

/**
 * 控制 Lua 垃圾收集器的行为。
 *
 * @param L Lua 状态机指针。
 * @param what 控制垃圾收集器的操作类型，可以是以下值之一：
 *   - LUA_GCSTOP: 停止垃圾收集器。
 *   - LUA_GCRESTART: 重新启动垃圾收集器。
 *   - LUA_GCCOLLECT: 强制执行一次完整的垃圾收集。
 *   - LUA_GCCOUNT: 返回当前内存使用量（以 KB 为单位）。
 *   - LUA_GCCOUNTB: 返回当前内存使用量的剩余字节部分（小于 1KB）。
 *   - LUA_GCSTEP: 执行一步垃圾收集，`data` 指定步长。
 *   - LUA_GCSETPAUSE: 设置垃圾收集器的暂停参数。
 *   - LUA_GCSETSTEPMUL: 设置垃圾收集器的步进乘数。
 *   - LUA_GCISRUNNING: 检查垃圾收集器是否正在运行。
 * @param data 操作所需的额外数据（具体含义取决于 `what` 参数）。
 * @return 根据操作类型返回不同的值：
 *   - LUA_GCCOUNT/LUA_GCCOUNTB: 返回内存使用量。
 *   - LUA_GCSTEP: 返回是否完成了一个垃圾收集周期。
 *   - LUA_GCSETPAUSE/LUA_GCSETSTEPMUL: 返回之前的参数值。
 *   - LUA_GCISRUNNING: 返回垃圾收集器的运行状态。
 *   - 其他操作通常返回 0。
 *   - 如果 `what` 无效，返回 -1。
 */
LUA_API int lua_gc(lua_State *L, int what, int data)
{
    int res = 0;
    global_State *g;
    lua_lock(L);
    g = G(L);
    switch (what) {
        case LUA_GCSTOP: {
            g->gcrunning = 0;
            break;
        }
        case LUA_GCRESTART: {
            luaE_setdebt(g, 0);
            g->gcrunning = 1;
            break;
        }
        case LUA_GCCOLLECT: {
            luaC_fullgc(L, 0);
            break;
        }
        case LUA_GCCOUNT: {
            /* GC values are expressed in Kbytes: #bytes/2^10 */
            res = cast_int(gettotalbytes(g) >> 10);
            break;
        }
        case LUA_GCCOUNTB: {
            res = cast_int(gettotalbytes(g) & 0x3ff);
            break;
        }
        case LUA_GCSTEP: {
            l_mem debt = 1;  /* =1 to signal that it did an actual step */
            lu_byte oldrunning = g->gcrunning;
            g->gcrunning = 1;  /* allow GC to run */
            if (data == 0) {
                luaE_setdebt(g, -GCSTEPSIZE);  /* to do a "small" step */
                luaC_step(L);
            } else {  /* add 'data' to total debt */
                debt = cast(l_mem, data) * 1024 + g->GCdebt;
                luaE_setdebt(g, debt);
                luaC_checkGC(L);
            }
            g->gcrunning = oldrunning;  /* restore previous state */
            if (debt > 0 && g->gcstate == GCSpause)  /* end of cycle? */
                res = 1;  /* signal it */
            break;
        }
        case LUA_GCSETPAUSE: {
            res = g->gcpause;
            g->gcpause = data;
            break;
        }
        case LUA_GCSETSTEPMUL: {
            res = g->gcstepmul;
            if (data < 40) data = 40;  /* avoid ridiculous low values (and 0) */
            g->gcstepmul = data;
            break;
        }
        case LUA_GCISRUNNING: {
            res = g->gcrunning;
            break;
        }
        default:
            res = -1;  /* invalid option */
    }
    lua_unlock(L);
    return res;
}

/*
** miscellaneous functions
*/

/**
 * 抛出一个 Lua 错误。
 * 此函数会触发 Lua 的错误处理机制，通常用于在 API 调用中报告错误。
 * 调用此函数后，控制流会立即跳转到最近的错误处理点（如 pcall 或 xpcall）。
 *
 * @param L Lua 状态机指针。
 * @return 永远不会实际返回（函数内部通过长跳转退出），但为了编译器警告返回 0。
 * @note 此函数会锁定 Lua 状态机，但在实际退出内核时会自动解锁。
 */
LUA_API int lua_error(lua_State *L)
{
    lua_lock(L);
    api_checknelems(L, 1);
    luaG_errormsg(L);
    /* code unreachable; will unlock when control actually leaves the kernel */
    return 0;  /* to avoid warnings */
}

/**
 * 遍历表中的键值对。
 *
 * 该函数用于遍历表（table）中的键值对。调用时，栈顶应为当前键（或 `nil` 表示从第一个键开始），
 * 函数会将下一个键值对压入栈顶（键在栈顶-2，值在栈顶-1），并返回是否还有更多键值对。
 *
 * @param L Lua 状态机指针。
 * @param idx 表的栈索引。
 * @return 如果还有更多键值对，返回 1；否则返回 0。
 *
 * @note 调用前需确保栈顶为当前键（或 `nil`）。
 * @note 如果返回 0，栈顶的键会被移除。
 */
LUA_API int lua_next(lua_State *L, int idx)
{
    StkId t;
    int more;
    lua_lock(L);
    t = index2addr(L, idx);
    api_check(L, ttistable(t), "table expected");
    more = luaH_next(L, hvalue(t), L->top - 1);
    if (more) {
        api_incr_top(L);
    } else  /* no more elements */
        L->top -= 1;  /* remove key */
    lua_unlock(L);
    return more;
}

/**
 * 将栈顶的 `n` 个值连接为一个字符串，并将结果压入栈顶。
 *
 * @param L Lua 状态机指针。
 * @param n 需要连接的值数量。
 *
 * 行为说明：
 * - 如果 `n >= 2`，调用 `luaV_concat` 执行连接操作。
 * - 如果 `n == 0`，将一个空字符串压入栈顶。
 * - 如果 `n == 1`，不执行任何操作（保留栈顶值）。
 *
 * 注意：
 * - 函数会触发垃圾回收检查（`luaC_checkGC`）。
 * - 操作完成后会释放 Lua 状态机的锁（`lua_unlock`）。
 */
LUA_API void lua_concat(lua_State *L, int n)
{
    lua_lock(L);
    api_checknelems(L, n);
    if (n >= 2) {
        luaV_concat(L, n);
    } else if (n == 0) {  /* push empty string */
        setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
        api_incr_top(L);
    }
    /* else n == 1; nothing to do */
    luaC_checkGC(L);
    lua_unlock(L);
}

/**
 * 获取指定索引处值的长度。
 * 该函数是Lua的API函数，用于获取表或字符串的长度。
 *
 * @param L Lua状态机指针。
 * @param idx 栈中值的索引。
 *
 * @note 该函数会锁定Lua状态机，确保线程安全。
 * @note 对于表，返回的是表的长度（通过`#`操作符获取的值）。
 * @note 对于字符串，返回的是字符串的字节长度。
 */
LUA_API void lua_len(lua_State *L, int idx)
{
    StkId t;
    lua_lock(L);
    t = index2addr(L, idx);
    luaV_objlen(L, L->top, t);
    api_incr_top(L);
    lua_unlock(L);
}

/**
 * 获取 Lua 状态机的内存分配器函数及其用户数据。
 *
 * @param L Lua 状态机指针。
 * @param ud 用于存储内存分配器的用户数据的指针（可选，可为 NULL）。
 * @return 返回当前 Lua 状态机使用的内存分配器函数。
 *
 * @note 此函数是线程安全的，内部会加锁保护全局状态。
 * @note 如果 `ud` 不为 NULL，则会将用户数据存储到 `*ud` 中。
 */
LUA_API lua_Alloc lua_getallocf(lua_State *L, void **ud)
{
    lua_Alloc f;
    lua_lock(L);
    if (ud) *ud = G(L)->ud;
    f = G(L)->frealloc;
    lua_unlock(L);
    return f;
}

/**
 * 设置 Lua 状态的内存分配函数。
 *
 * @param L Lua 状态指针。
 * @param f 自定义的内存分配函数，用于替换 Lua 默认的内存分配逻辑。
 * @param ud 用户数据，将作为参数传递给内存分配函数 `f`。
 *
 * @note 此函数是线程安全的，调用时会自动加锁。
 * @note 通过此函数可以自定义 Lua 的内存管理行为，适用于需要特殊内存管理的场景。
 */
LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud)
{
    lua_lock(L);
    G(L)->ud = ud;
    G(L)->frealloc = f;
    lua_unlock(L);
}

/**
 * 创建一个新的用户数据对象并返回其内存指针。
 *
 * @param L Lua状态机指针。
 * @param size 用户数据对象的内存大小（字节）。
 * @return 指向用户数据对象内存的指针。
 *
 * @note 此函数是Lua C API的一部分，用于在Lua中分配和管理用户数据。
 *       用户数据是Lua中用于存储自定义C数据的机制。
 *       函数内部会触发垃圾回收检查（GC）。
 */
LUA_API void *lua_newuserdata(lua_State *L, size_t size)
{
    Udata *u;
    lua_lock(L);
    u = luaS_newudata(L, size);
    setuvalue(L, L->top, u);
    api_incr_top(L);
    luaC_checkGC(L);
    lua_unlock(L);
    return getudatamem(u);
}

/**
 * 获取闭包（closure）的第n个上值（upvalue）及其相关信息。
 *
 * @param fi 栈索引，指向闭包对象。
 * @param n 上值的索引（从1开始）。
 * @param val 用于存储上值的指针。
 * @param owner 如果是C闭包，用于存储闭包对象的指针（可选）。
 * @param uv 如果是Lua闭包，用于存储上值对象的指针（可选）。
 * @return 返回上值的名称字符串。如果是C闭包，返回空字符串；如果是Lua闭包且无名称，返回"(*no name)"；如果不是闭包，返回NULL。
 *
 * @note 该函数是静态的，仅供内部使用。
 */
static const char *aux_upvalue(StkId fi, int n, TValue **val,
                               CClosure **owner, UpVal **uv)
{
    switch (ttype(fi))
    {
    case LUA_TCCL:
    { /* C closure */
        CClosure *f = clCvalue(fi);
        if (!(1 <= n && n <= f->nupvalues))
            return NULL;
        *val = &f->upvalue[n - 1];
        if (owner)
            *owner = f;
        return "";
    }
    case LUA_TLCL:
    { /* Lua closure */
        LClosure *f = clLvalue(fi);
        TString *name;
        Proto *p = f->p;
        if (!(1 <= n && n <= p->sizeupvalues))
            return NULL;
        *val = f->upvals[n - 1]->v;
        if (uv)
            *uv = f->upvals[n - 1];
        name = p->upvalues[n - 1].name;
        return (name == NULL) ? "(*no name)" : getstr(name);
    }
    default:
        return NULL; /* not a closure */
    }
}

/**
 * 获取指定函数的上值（upvalue）的名称和值。
 *
 * @param L Lua状态机指针。
 * @param funcindex 函数在栈中的索引。
 * @param n 上值的序号（从1开始）。
 * @return 返回上值的名称；如果上值不存在，则返回NULL。
 *
 * @note 如果上值存在，其值会被压入栈顶。
 * @warning 调用此函数时需确保Lua状态机已锁定。
 */
LUA_API const char *lua_getupvalue(lua_State *L, int funcindex, int n)
{
    const char *name;
    TValue *val = NULL; /* to avoid warnings */
    lua_lock(L);
    name = aux_upvalue(index2addr(L, funcindex), n, &val, NULL, NULL);
    if (name) {
        setobj2s(L, L->top, val);
        api_incr_top(L);
    }
    lua_unlock(L);
    return name;
}

/**
 * 设置函数的上值（upvalue）。
 *
 * @param L Lua状态机指针。
 * @param funcindex 函数在栈中的索引。
 * @param n 上值的序号（从1开始）。
 * @return 返回上值的名称，如果操作失败则返回NULL。
 *
 * @note 此函数用于修改闭包的上值。调用时需确保栈顶有一个新值用于替换当前上值。
 *       如果上值属于闭包（CClosure），则会触发屏障（barrier）以确保垃圾回收的正确性。
 *       如果上值属于UpVal对象，则会触发上值屏障（upvalbarrier）。
 */
LUA_API const char *lua_setupvalue(lua_State *L, int funcindex, int n)
{
    const char *name;
    TValue *val = NULL; /* to avoid warnings */
    CClosure *owner = NULL;
    UpVal *uv = NULL;
    StkId fi;
    lua_lock(L);
    fi = index2addr(L, funcindex);
    api_checknelems(L, 1);
    name = aux_upvalue(fi, n, &val, &owner, &uv);
    if (name) {
        L->top--;
        setobj(L, val, L->top);
        if (owner) { luaC_barrier(L, owner, L->top); }
        else if (uv) { luaC_upvalbarrier(L, uv); }
    }
    lua_unlock(L);
    return name;
}

/**
 * 获取指定 Lua 函数的上值（upvalue）的引用。
 *
 * @param L Lua 状态机指针。
 * @param fidx 函数在栈中的索引。
 * @param n 上值的索引（从 1 开始）。
 * @return 返回指向上值的指针。
 *
 * @note 如果 `fidx` 不是 Lua 函数或 `n` 不是有效的上值索引，会触发 API 检查错误。
 */
static UpVal **getupvalref(lua_State *L, int fidx, int n)
{
    LClosure *f;
    StkId fi = index2addr(L, fidx);
    api_check(L, ttisLclosure(fi), "Lua function expected");
    f = clLvalue(fi);
    api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index");
    return &f->upvals[n - 1];  /* get its upvalue pointer */
}

/**
 * 获取指定闭包的上值（upvalue）的标识符。
 *
 * @param L Lua状态机指针。
 * @param fidx 闭包在栈中的索引。
 * @param n 上值的索引（从1开始）。
 * @return 返回上值的标识符指针。如果闭包无效或索引越界，返回NULL。
 *
 * @note 该函数用于获取Lua闭包或C闭包的上值标识符。
 *       对于Lua闭包，返回的是上值的引用；对于C闭包，返回的是上值的地址。
 *       如果传入的索引无效或闭包类型不符合预期，会触发API检查错误。
 */
LUA_API void *lua_upvalueid(lua_State *L, int fidx, int n)
{
    StkId fi = index2addr(L, fidx);
    switch (ttype(fi))
    {
    case LUA_TLCL:
    { /* lua closure */
        return *getupvalref(L, fidx, n);
    }
    case LUA_TCCL:
    { /* C closure */
        CClosure *f = clCvalue(fi);
        api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index");
        return &f->upvalue[n - 1];
    }
    default:
    {
        api_check(L, 0, "closure expected");
        return NULL;
    }
    }
}

/**
 * 将两个上值（upvalue）合并为一个。
 *
 * 该函数用于将两个不同的上值合并，使得它们引用同一个值。主要用于优化或简化闭包中的上值引用。
 *
 * @param L Lua状态机指针。
 * @param fidx1 第一个闭包在栈中的索引。
 * @param n1 第一个闭包中上值的索引。
 * @param fidx2 第二个闭包在栈中的索引。
 * @param n2 第二个闭包中上值的索引。
 *
 * @note 如果两个上值已经相同，则函数直接返回。
 * @note 调用此函数后，第一个上值的引用计数会减少，第二个上值的引用计数会增加。
 * @note 如果第二个上值是开放的（open），则标记其为已修改。
 */
LUA_API void lua_upvaluejoin(lua_State *L, int fidx1, int n1,
                             int fidx2, int n2)
{
    UpVal **up1 = getupvalref(L, fidx1, n1);
    UpVal **up2 = getupvalref(L, fidx2, n2);
    if (*up1 == *up2)
        return;
    luaC_upvdeccount(L, *up1);
    *up1 = *up2;
    (*up1)->refcount++;
    if (upisopen(*up1)) (*up1)->u.open.touched = 1;
    luaC_upvalbarrier(L, *up1);
}
